//
// Created by lxx on 23-1-11.
//

#include "MultiPanelRectangleGridRayTracing.cuh"
#include <algorithm/rayTracing/dataLoader/CloudLoader.h>
#include <algorithm/rayTracing/rayTracingDataStructure/receiver/CudaMultiPanelReceiver.cuh>
#include <algorithm/rayTracing/rayTracingPipeline/quasiMonteCarloRayTracing/Grid/rectGridDDA.cuh>
#include <algorithm/shared/tool/randomNumberGenerator/RandomGenerator.cuh>


#include <cstdio>

using namespace solar::cuda;

__global__ void test_map_raytracing(SunrayArgument sunrayArgument,
                                    CudaMultiPanelReceiver multiPanelReceiver)
{
    printf("---------enter multi panel:: test_map_raytracing()-------------\n");
}
/*
    ！！！！！！！！！rectangleGrid有问题，检查一下
*/
__global__ void map_raytracing(SunrayArgument sunrayArgument,
                               CudaMultiPanelReceiver multiPanelReceiver,
                               CudaRectangleGrid rectangleGrid, HeliostatArgument heliostatArgument,
                               float3* d_subheliostat_vertexes, float factor, bool res,
                               solar::CloudLoader cloud)
{

    // printf("-------- enter multi panel:: map_raytracing()---------\n");
    int my_id = global_func::getThreadID(); // 线程ID
    int number_of_micro_heliostats =
        heliostatArgument.micronums * heliostatArgument.helionums; // 所有微定日镜个数
    if (my_id >= number_of_micro_heliostats * sunrayArgument.numberOfLightsPerGroup)
    {
        return; // id比光线数目大，返回
    }
    int microsize = heliostatArgument.micronums; // 每个定日镜的微定日镜个数
    int no = my_id / microsize / sunrayArgument.numberOfLightsPerGroup; // 第几号镜子

    // int is_total_reflect=0;
    float depth_ = 0.003f;
    float refract_ = 1.523f;
    int helio_no =
        my_id / sunrayArgument.numberOfLightsPerGroup / heliostatArgument.micronums; // 第几个定日镜
    int micro_no = my_id / sunrayArgument.numberOfLightsPerGroup %
                   heliostatArgument.micronums; // 该定日镜的第几个微定日镜

    int substart = helio_no * 3;

    float3 helio_pos =
        (d_subheliostat_vertexes[substart] + d_subheliostat_vertexes[substart + 2]) / 2;
    float3 helio_norm = normalize(
        cross(d_subheliostat_vertexes[substart] - d_subheliostat_vertexes[substart + 1],
              d_subheliostat_vertexes[substart + 2] - d_subheliostat_vertexes[substart + 1]));

    int col_no = micro_no / heliostatArgument.col;
    int row_no = micro_no % heliostatArgument.col;
    float3 micropos = helio_pos;
    float3 micronorm = helio_norm;

    micropos.z = col_no * heliostatArgument.pixel + heliostatArgument.pixel / 2 -
                 heliostatArgument.sizex / 2;
    micropos.y = heliostatArgument.sizey / 2;
    micropos.x = row_no * heliostatArgument.pixel + heliostatArgument.pixel / 2 -
                 heliostatArgument.sizez / 2;

    float3 local = micropos;
    local = global_func::local2world_rotate(local, micronorm);
    local = global_func::translate(local, helio_pos);
    micropos = local;

    int address = (my_id) % sunrayArgument.pool_size;

    // 采样数组的采样值
    float3 dir = global_func::local2world_rotate(sunrayArgument.d_samplelights[address],
                                                 -sunrayArgument.sunray_direction);
    // 入射光方向（世界坐标系）
    //    dir = -sunrayArgument.sunray_direction;
    // 计算第helio_no个定日镜的第micro_no个微定日镜的法向和坐标

    float3 origin = micropos; // 微定日镜中心坐标

    if (rectGridDDA::collision_(origin, dir, rectangleGrid, d_subheliostat_vertexes,
                                heliostatArgument, no))
    {
        return;
    }

    float3 normal = micronorm; // 微定日镜法向方向，局部坐标系
    address = (my_id) % sunrayArgument.pool_size;
    float3 normal1 = global_func::local2world_rotate(sunrayArgument.d_perturbations[address],
                                                     normal); // 微定日镜法向方向，世界坐标系
    //    normal1 = normal;//微定日镜法向方向，世界坐标系
    normal = normalize(normal1);
    //    normal = normalize(normal);

    // 先和其他定日镜求交,看是否被遮挡
    // 如果没有被遮挡,求光线与云的交点,如果在数组中,取数组中的值,不在,取1.返回值是float值
    //    float3 dir_n= make_float3(0.0,1.0,0.0);
    //    float cos_theta= dot(dir,dir_n);
    //    float len=cloud.start_point.y/cos_theta;
    //    float3 dirs=dir*len;
    //    float2 M= make_float2(origin.x+dirs.x ,origin.z+dirs.z );
    //    float times=cloud.start_point.y/-sunrayArgument.sunray_direction.y;
    //    float2 cloudcenter=
    //    make_float2(-sunrayArgument.sunray_direction.x*times,-sunrayArgument.sunray_direction.z*times);
    //    float2
    //    cloudstart=make_float2(cloudcenter.x+cloud.start_point.x,cloudcenter.y+cloud.start_point.z);
    //    float my=M.x-cloudstart.x;//M.x, M.y 是M点在世界坐标系的（x,z)坐标值
    //    float mx=M.y-cloudstart.y;//需要转换成 cloud map局部坐标
    //    /*世界坐标  --> X
    //     *         |
    //     *         |
    //     *         Z
    //     * cloud map 局部坐标  --> y
    //     *                    |
    //     *                    |
    //     *                    x
    //     * M.x-cloud.start_point.x M点在局部坐标系的第几列
    //     * M.y-cloud.start_point.z M点在局部坐标系的第几行
    //     * A    B
    //     *    M
    //     * D    C
    //     * 根据A,B,C,D四点的值双线性插值得到M的值
    //     */
    //    int Ax= floor(mx/cloud.pixel);//A在cloud map中第几行
    //    int Ay= floor(my/cloud.pixel);//A在cloud map的第几列
    //    float decay=1.0;

    //     //先和其他定日镜求交,看是否被遮挡
    //     //如果没有被遮挡,求光线与云的交点,如果在数组中,取数组中的值,不在,取1.返回值是float值
    //     float3 dir_n= make_float3(0.0,1.0,0.0);
    //     float cos_theta=dot(dir, dir_n);
    // //    float3 dirs=dir*len;
    //     float3 dirs=dir;

    //     int cloud_type=1;//0 is online, 1 is vertical, 2 is horizontal

    //     float2 cloudstart;
    //     float len=0;
    //     if(cloud_type==0){
    //         len=cloud.start_point.y/cos_theta;
    //         dirs=dirs*len;
    //         float times=cloud.start_point.y/-sunrayArgument.sunray_direction.y;
    //         float2 cloudcenter=
    //         make_float2(-sunrayArgument.sunray_direction.x*times,-sunrayArgument.sunray_direction.z*times);
    //         cloudstart=make_float2(cloudcenter.x+cloud.start_point.x,cloudcenter.y+cloud.start_point.z);
    // //        printf("%f %f \n",cloudstart.x,cloudstart.y);
    //     }
    //     else if(cloud_type==1){
    // //        float height=0;
    //         len=cloud.start_point.y/cos_theta;
    //         dirs=dirs*len;
    // //        float times=height/-sunrayArgument.sunray_direction.y;
    // //        float2 cloudcenter=
    // make_float2(-sunrayArgument.sunray_direction.x*times,-sunrayArgument.sunray_direction.z*times);
    //         float2 cloudcenter= make_float2(0,0);
    //         cloudstart=make_float2(cloudcenter.x+cloud.start_point.x,cloudcenter.y+cloud.start_point.z);
    // //        printf("%f %f \n",cloudstart.x,cloudstart.y);

    //     }

    //     float2 M= make_float2(origin.x+dirs.x,origin.z+dirs.z);

    //     float my=M.x-cloudstart.x;//M.x, M.y 是M点在世界坐标系的（x,z)坐标值
    //     float mx=M.y-cloudstart.y;//需要转换成 cloud map局部坐标
    //     int Ax= floor(mx/cloud.pixel);//A在cloud map中第几行
    //     int Ay= floor(my/cloud.pixel);//A在cloud map的第几列
    // //    printf("Ax %d Ay %d  cloudstart %f %f len %f\n",Ax,Ay,cloudstart.x,cloudstart.y,len);
    //     float decay=1.0;

    //     if(Ax<=0||Ax>cloud.x_size||Ay<0||Ay>cloud.y_size){
    //         decay=1.0;
    // //      printf("times %f cloudstart %f %f %f %f \n",times,cloudstart.x,cloudstart.y,mx,my);
    //     }
    //     else{
    // //        printf("my %f mx %f\n",my,mx);
    //         /*世界坐标  --> X
    //          *         |
    //          *         |
    //          *         Z
    //          * cloud map 局部坐标  --> y
    //          *                    |
    //          *                    |
    //          *                    x
    //          * M.x-cloud.start_point.x M点在局部坐标系的第几列
    //          * M.y-cloud.start_point.z M点在局部坐标系的第几行
    //          * A    B
    //          *    M
    //          * D
    //          * 根据A,B,C,D四点的值双线性插值得到M的值
    //          */

    //         int A_pos=Ay+Ax*cloud.x_size;

    //         float Q11=cloud.d_cloudMap[A_pos];                  //A
    //         float Q12=cloud.d_cloudMap[A_pos+1];                //B
    //         float Q21=cloud.d_cloudMap[A_pos+cloud.x_size];     //D
    //         float Q22=cloud.d_cloudMap[A_pos+cloud.x_size+1];   //C
    //         //A x1,y1
    //         //B x1,y2
    //         //C x2,y2
    //         //D x2,y1
    //         float x1=Ax*cloud.pixel;
    //         float x2=(Ax+1)*cloud.pixel;
    //         float y1=Ay*cloud.pixel;
    //         float y2=(Ay+1)*cloud.pixel;

    //         decay=(Q11*(x2-mx)*(y2-my)+Q21*(mx-x1)*(y2-my)+Q12*(x2-mx)*(my-y1)+Q22*(mx-x1)*(my-y1))/cloud.pixel/cloud.pixel;
    // //        decay=1.0-decay;
    //     }

    dir = normalize(reflect(-dir, normal));

    if (rectGridDDA::collision_(origin, dir, rectangleGrid, d_subheliostat_vertexes,
                                heliostatArgument, no))
    {
        return;
    }

    // Step 3: intersect with receiver and calculate energy.
    //    float t, u, v;

    int panel_no = 0, tube_no = 0;
    float t = 0, u = 0;
    if (!multiPanelReceiver.GIntersect(origin, dir, t, u, panel_no, tube_no))
    {
        return;
    }
    // Step 2: Calculate the energy of the light

    //    if (::isnan(t))printf("test\n");
    // Step 2: Calculate the energy of the light
    float eta = 0.0;
    if (t <= 1000.0f) {
        eta = 0.99331F - 0.0001176F * t + 1.97F * (1e-8F) * t * t;
    } else {
        eta = std::exp(-0.0001106F * t);
}

    float decay = 1.0;
    float energy = fabsf(dot(dir, normal)) * eta * factor * decay;
    //    if(energy==NAN){
    //        return;
    //    }
    //    printf("dir %f %f %f t %f energy %f  panel %d tube %d angle %d
    //    \n",dir.x,dir.y,dir.z,t,energy,panel_no,tube_no,angle_no); if
    //    (tube_no==0&&tube_no==61)energy=0.0;
    // Step 3: Add the energy to the intersect position
    // Intersect position

    int cols = panel_no * (multiPanelReceiver.tube_ * 2) + tube_no;
    u = 1.0 - u;
    int rows = u * multiPanelReceiver.getResolution().y;
    if (rows >= multiPanelReceiver.getResolution().y ||
        cols >= multiPanelReceiver.getResolution().x)
    {
        //        printf("%d \n",rows);
        return;
    }
    int2 row_col = make_int2(rows, cols);
    int adde = row_col.x * multiPanelReceiver.getResolution().x +
               row_col.y; // col_row.y + col_row.x * resolution.y
                          //    adde=int(0);
                          //    energy=1.0f;
    float* image = multiPanelReceiver.getDeviceImage();
    atomicAdd(&(image[adde]), energy); // CUDA atomic
}
void solar::cuda::MultiPanelReceiverRectangleGridRayTracing(
    SunrayArgument& sunrayArgument, CudaMultiPanelReceiver* multiPanelReceiver,
    CudaRectangleGrid* rectGrid, HeliostatArgument& heliostatArgument,
    float3* d_subHeliostat_vertexes, float factor, bool res, CloudLoader* cloud)
{
    int n_threads = 256;
    dim3 n_blocks;
    long long int sizes = (long long)heliostatArgument.helionums * heliostatArgument.micronums *
                          (long long)sunrayArgument.numberOfLightsPerGroup;
    printf("sizes  %lld \n", sizes);
    global_func::setThreadBlocks(n_blocks, n_threads, sizes, true);
    printf("nBlocks = %d %d %d, nThreads = %d\n", n_blocks.x, n_blocks.y, n_blocks.z, n_threads);
    printf(" rectGrid type = %d\n", rectGrid->getGridType());

    //    printf("num of rays= %lld \n",heliostatArgument.helionums*heliostatArgument.micronums *
    //    sunrayArgument.numberOfLightsPerGroup); printf("%lld\n",
    //    heliostatArgument.numberOfMicroHeliostats * sunrayArgument.numberOfLightsPerGroup);

    map_raytracing<<<n_blocks, n_threads>>>(sunrayArgument, *multiPanelReceiver, *rectGrid,
                                          heliostatArgument, d_subHeliostat_vertexes, factor, res,
                                          *cloud);

    cudaDeviceSynchronize();
    checkCudaErrors(cudaGetLastError());
}
